/*
 * Copyright (C) 2009 LianHaidong <lianhaidong@gmail.com>
 */

#include <stdio.h>
#include <string.h>
#include "utils/dfa.h"
#include "utils/debug.h"
#include "utils/list.h"

static struct dfa_state_shift *dfa_state_shift_create(char *cmd, 
			struct dfa_state *src, struct dfa_state *dst, 
			dfa_shift_func match, void *priv)
{
	struct dfa_state_shift *shift;

	shift = malloc(sizeof(*shift));
	if (!shift) {
		msg("Outof Memory !!!");
		return NULL;
	}

	shift->cmd = cmd;
	shift->state = src;
	shift->shift_state = dst;
	INIT_LIST_HEAD(&shift->link);
	shift->match = match;
	shift->priv = priv;

	return shift;
}

static inline void dfa_state_shift_delete(struct dfa_state_shift *shift)
{
	free(shift);
}

static struct dfa_state *dfa_state_create(struct dfa *dfa, char *name)
{
	struct dfa_state *state;

	state = malloc(sizeof(*state));
	if (!state) {
		msg("Outof memory !!!");
		return NULL;
	}

	state->name = name;
	state->dfa = dfa;
	state->shift_count = 0;
	INIT_LIST_HEAD(&state->shift_head);

	return state;
}

static void dfa_state_delete(struct dfa_state *state) {
	struct dfa_state_shift *shift, *shift1;

	list_for_each_entry_safe(shift, shift1, &state->shift_head, link) {
		dfa_state_shift_delete(shift);
	}

	free(state);
}

static void dfa_state_add_shift(struct dfa_state *state, struct dfa_state_shift *shift)
{
	list_add_tail(&shift->link, &state->shift_head);

	state->shift_count ++;
}

struct dfa *dfa_create(char *name)
{
	struct dfa *dfa;

	dfa = malloc(sizeof(*dfa));

	if (!dfa) {
		msg("Outof Memory !!!");
		return NULL;
	}

	dfa->name = name;
	dfa->start_state.name = "start";
	INIT_LIST_HEAD(&dfa->start_state.shift_head);
	dfa->start_state.shift_count = 0;
	
	dfa->state_count = 0;
	INIT_LIST_HEAD(&dfa->state_head);
	INIT_LIST_HEAD(&dfa->obj_head);
	INIT_LIST_HEAD(&dfa->link);

	return dfa;
}

void dfa_delete(struct dfa *dfa) 
{
	struct dfa_state *state, *state1;
	struct dfa_object *obj, *obj1;

	list_for_each_entry_safe(state, state1, &dfa->state_head, link) {
		dfa_state_delete(state);
	}

	list_for_each_entry_safe(obj, obj1, &dfa->obj_head, link) {
		dfa_del_object(dfa, obj);
	}

	free(dfa);
}

struct dfa_object *dfa_new_object(struct dfa *dfa, char *name)
{
	struct dfa_object *obj;

	obj = malloc(sizeof(*obj));
	if (!obj) {
		msg("Outof memory !!!");
		return NULL;
	}

	memset(obj, 0, sizeof(*obj));
	obj->name = name;
	obj->dfa = dfa;
	obj->current_state = &dfa->start_state;
	list_add_tail(&obj->link, &dfa->obj_head);

	return obj;
}

int dfa_del_object(struct dfa *dfa, struct dfa_object *obj)
{
	if (obj->dfa != dfa) {
		return -1;
	}

	list_del(&obj->link);
	free(obj);
	return 0;
}

static struct dfa_state *dfa_state_from_name(struct dfa *dfa, const char *name)
{
	struct dfa_state *state;

	if (strcmp(name, dfa->start_state.name) == 0)
		return &dfa->start_state;
	list_for_each_entry(state, &dfa->state_head, link) {
		if (strcmp(name, state->name) == 0) {
			return state;
		}
	}

	return NULL;
}

int dfa_add_rule(struct dfa *dfa, char *src_name, char *cmd, char *dst_name,
		dfa_shift_func match)
{
	struct dfa_state *src, *dst;
	struct dfa_state_shift *shift;

again:
	src = dfa_state_from_name(dfa, src_name);
	if (!src) {
		dfa_add_state(dfa, src_name);
		goto again;
	}

again1:
	dst = dfa_state_from_name(dfa, dst_name);
	if (!dst) {
		dfa_add_state(dfa, dst_name);
		goto again1;
	}

	shift = dfa_state_shift_create(cmd, src, dst, match, NULL);
	if (!shift) {
		msg("Outof memory!!!");
		return -1;
	}

	dfa_state_add_shift(src, shift);
	
	return 0;
}

int dfa_add_state(struct dfa *dfa, char *state_name)
{
	struct dfa_state *state;

	state = dfa_state_create(dfa, state_name);
	if (!state) {
		msg("dfa_state_create fail !!!");
		return -1;
	}

	list_add_tail(&state->link, &dfa->state_head);
	dfa->state_count ++;
	return 0;
}

int dfa_object_reset(struct dfa_object *obj)
{
	obj->current_state = &obj->dfa->start_state;
	return 0;
}

int dfa_object_put_cmd(struct dfa_object *obj, const char *cmd, void *arg)
{
	struct dfa_state_shift *shift;

	list_for_each_entry(shift, &obj->current_state->shift_head, link) {
		if (shift->match(shift, cmd, arg) == 0) {
			goto found;
		}
	}

	msg(" ");
	return -1;

found:
	obj->current_state = shift->shift_state;

	return 0;
}



